/**
 * Copyright 2010 Tristan Slominski
 *
 * Licensed under the MIT License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.opensource.org/licenses/mit-license.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mindash.handlebars.client.services.impl;

import java.util.Map;

import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.inject.Inject;
import com.mindash.handlebars.client.events.ReloadEvent;
import com.mindash.handlebars.client.places.AtPlace;
import com.mindash.handlebars.client.places.GoToPlace;
import com.mindash.handlebars.client.providers.GoToPlaceProvider;
import com.mindash.handlebars.client.services.EventBus;
import com.mindash.handlebars.client.services.PlaceService;
import com.mindash.handlebars.client.services.utils.PlaceServiceUtils;
import com.mindash.handlebars.client.wrappers.HistoryWrapper;

/**
 * @author Tristan Slominski
 * 
 */
public abstract class DefaultPlaceServiceImpl implements PlaceService {

  public static class ParamCoder {
    public static String encode(String urlParam) {
      String result = urlParam;
      result = result.replace("?", "%3F");
      result = result.replace("&", "%26");
      result = result.replace("=", "%3D");
      return result;
    }

    public static String decode(String urlParam) {
      String result = urlParam;
      result = result.replace("%3F", "?");
      result = result.replace("%26", "&");
      result = result.replace("%3D", "=");
      return result;
    }
  }

  /* injected dependencies */
  @Inject
  private EventBus eventBus;
  @SuppressWarnings("unused")
  @Inject
  private GoToPlaceProvider goToPlaceProvider;
  @Inject
  private HistoryWrapper history;
  @Inject
  private PlaceServiceUtils utils;

  /* variables */
  private AtPlace<?> currentPlace;

  /**
   * <p>
   * When implementing place service, this method should create place object
   * based on the place name.
   * </p>
   * <p>
   * for example:
   * </p>
   * <code><pre>
   * AtPlace&lt;?&gt; place = null;
   * if (MyPlaces.landingPage.equals(placeName)) {
   *   place = myAtPlaceProvider.getAtLandingPage();
   * } else if (MyPlaces.dashboardPage.equals(placeName)) {
   *   place = myAtPlaceProvider.getAtDashboardPage();
   * } else {
   *   GWT.log("could not identify place '" + placeName + "'",
   *       new IllegalArgumentException());
   * }
   * return place;
   * </pre></code>
   * 
   * @param placeName the name of the place the application could be at
   * @return the place object for the particular {@code placeName}
   */
  public abstract AtPlace<?> createThePlaceObject(String placeName);

  /**
   * <p>
   * When implementing place service, this method should contain all the
   * GoToPlace handler registrations.
   * </p>
   * 
   * <p>
   * for example:
   * </p>
   * <code><pre>
   * eventBus.addHandler(MyGoToPlaceProvider.goToLandingPageType, this);
   * </pre></code>
   * 
   * @param eventBus the application event bus to register with
   */
  public abstract void registerHandlersToAllPlaces(EventBus eventBus);

  @Override
  public boolean atPlaceEquals(AtPlace<?> atPlace) {
    if (atPlace == null) {
      return false;
    }
    return atPlace.asTargetHistoryToken().equals(
        getCurrentPlace().asTargetHistoryToken());
  }

  @Override
  public void initialize() {
    history.addValueChangeHandler(this);

    // register reload event handler
    eventBus.addHandler(new ReloadEvent().getAssociatedType(), this);

    // register handlers to all places
    registerHandlersToAllPlaces(eventBus);

    // launch current place
    history.fireCurrentHistoryState();
  }

  @Override
  public void onGoToPlace(GoToPlace<?> place) {
    String queryString = utils.paramsToUrlString(place.getParams());
    history.newItem(place.getPlaceHistoryToken() + queryString);
  }

  @Override
  public void onReloadEvent() {
    history.fireCurrentHistoryState();
  }

  @Override
  public void onValueChange(ValueChangeEvent<String> event) {

    String token = event.getValue();
    if (token == null) {
      return;
    }

    // prepare place name and parse url parameters
    String placeName = utils.getPlaceName(token);
    Map<String, String> params = utils.parseUrlParams(token);
    AtPlace<?> place = null;

    // create the place object
    place = createThePlaceObject(placeName);

    // set the parameters and fire the event
    if (place != null) {
      place.setParams(params);
      setCurrentPlace(place);
      eventBus.fireEvent(place);
    }

    return;
  }

  /* getters and setters */

  public AtPlace<?> getCurrentPlace() {
    return currentPlace;
  }

  public void setCurrentPlace(AtPlace<?> currentPlace) {
    this.currentPlace = currentPlace;
  }

}
